/*
 * face.cpp
 *
 *  Created on: 16/11/2011
 *      Author: Aeradon
 */

#include "../include/face.hpp"

#ifdef _DEBUG
	#include <cstdlib>
#endif

int Face::markCount(1);

Face::Face():
	vecIndex(0),
	mark(0),
	isBoundary(false),
	node(nullptr)
{

}

Face::~Face()
{

}

bool Face::IsMarked()
{
	return (mark == markCount);
}

void Face::ResetMark()
{
	++markCount;
	if (markCount > 0x7FFFFFFE)
		markCount = 1;
}

void Face::Mark()
{
	mark = markCount;
}

void Face::setEdges(S* e1, S* e2, S* e3)
{
	// Set edges is used to change some triangles, must be cleared
	v_edge.clear();

	V* e1Org = e1->vtx();
	V* e2Org = e2->vtx();
	V* e3Org = e3->vtx();

	e1->setFac(this);
	e2->setFac(this);
	e3->setFac(this);

	// If vertices arent in ccw order, v1 and v2 must be fliped
	if (Triangulation::ccw(e1Org->x,e1Org->y,e2Org->x,e2Org->y,e3Org->x,e3Org->y) <= 0)
	{
		//ccw
		//store adjacent edges in ccw order
		v_edge.push_back(e1);
		v_edge.push_back(e2);
		v_edge.push_back(e3);

		//linking the circular ccw edge Nxt pointers
		e1->setNxt(e2);
		e2->setNxt(e3);
		e3->setNxt(e1);

		// link the  circular ccw edge Rot pointers
		e1->setRot(e3->sym());
		e2->setRot(e1->sym());
		e3->setRot(e2->sym());

	}
	else // This comes from the point in face insertion.
	{
		//store the adjacent half edges in the vector
		v_edge.push_back(e1);
		v_edge.push_back(e3);
		v_edge.push_back(e2);

		//linking the circular ccw edge pointers
		e1->setNxt(e3);
		e3->setNxt(e2);
		e2->setNxt(e1);

		e1->setRot(e2->sym());
		e2->setRot(e3->sym());
		e3->setRot(e1->sym());
	}

}

void Face::setEdges(const std::vector<S*>& symEdges)
{
	v_edge = symEdges;

	// XXX Beware: The logic for setting this symEdges will probably
	// only work for the outer bound of the triangulation
	for (unsigned int i = 0 ; i < symEdges.size()-1; i++)
	{
		symEdges[i]->setNxt(symEdges[i+1]);
		symEdges[i+1]->setRot(symEdges[i]->sym());
	}

	symEdges[0]->setRot(symEdges[symEdges.size()-1]->sym());
	symEdges[symEdges.size()-1]->setNxt(symEdges[0]);
}

bool Face::colidesWithCircle(P point, T radius)
{
	//TEST 1: Triangle vertex within circle
	static T radiusSqr;
	radiusSqr = radius * radius;
	static P a_Pc[MAX_EDGES];
	static T a_cSqr[MAX_EDGES];
	for(unsigned int i = 0; i < v_edge.size(); ++i)
	{
		a_Pc[i].setXY(point.x - v_edge[i]->vtx()->x, point.y - v_edge[i]->vtx()->y);
		a_cSqr[i] = a_Pc[i].x * a_Pc[i].x + a_Pc[i].y * a_Pc[i].y - radiusSqr;

		if (a_cSqr[i] <= 0.0f)
			return true;
	}

	//TEST 2: Circle center within triangle
	//Calculate edges
	static P a_eXY[MAX_EDGES];
	for(unsigned int i = 0; i < v_edge.size() -1; ++i)
	{
		a_eXY[i].setXY(v_edge[i+1]->vtx()->x - v_edge[i]->vtx()->x, v_edge[i+1]->vtx()->y - v_edge[i]->vtx()->y);
	}
	a_eXY[v_edge.size()].setXY(v_edge[0]->vtx()->x - v_edge[v_edge.size()-1]->vtx()->x, v_edge[0]->vtx()->y - v_edge[v_edge.size()-1]->vtx()->y);

	static bool isInside;
	isInside = true;
	for(unsigned int i = 0; i < v_edge.size(); ++i)
	{
		if (a_eXY[i].y * a_Pc[i].x < a_eXY[i].x * a_Pc[i].y)
		{
			isInside = false;
			break;
		}
	}
	if (isInside)
		return true;

	//TEST 3: Circle intersects edge
	for(unsigned int i = 0; i < v_edge.size(); ++i)
	{
		static T k;
		k = a_Pc[i].x * a_eXY[i].x + a_Pc[i].y * a_eXY[i].y;

		if (k > Triangulation::EPSILON)
		{
			static T len;
			len = a_eXY[i].x * a_eXY[i].x + a_eXY[i].y * a_eXY[i].y;

			if (k < len)
			{
				if (a_cSqr[i] * len <= k * k)
					return true;
			}
		}
	}
	return false;
}

void Face::FindEdgesColidingWithCircle(P point, T radius, std::set<SymEdge*>& s_edges)
{
	//TEST 1: Triangle vertex within circle
	static T radiusSqr;
	radiusSqr = radius * radius;
	static P a_Pc[MAX_EDGES];
	static T a_cSqr[MAX_EDGES];

	for(unsigned int i = 0; i < v_edge.size(); ++i)
	{
		a_Pc[i].setXY(point.x - v_edge[i]->vtx()->x, point.y - v_edge[i]->vtx()->y);
		a_cSqr[i] = a_Pc[i].x * a_Pc[i].x + a_Pc[i].y * a_Pc[i].y - radiusSqr;

		if (a_cSqr[i] <= 0.0f) // Collided
		{
			s_edges.insert(v_edge[i]);
			s_edges.insert(v_edge[i]->nxt()->nxt());
			return;
		}
	}

	//Calculate edges
	static P a_distCirCenterEdge[MAX_EDGES]; // distancia centro circulo ate proximo + prox da reta (faznedo 90)
	for(unsigned int i = 0; i < v_edge.size() -1; ++i)
	{
		a_distCirCenterEdge[i].setXY(v_edge[i+1]->vtx()->x - v_edge[i]->vtx()->x, v_edge[i+1]->vtx()->y - v_edge[i]->vtx()->y);
	}
	a_distCirCenterEdge[v_edge.size()-1].setXY(v_edge[0]->vtx()->x - v_edge[v_edge.size()-1]->vtx()->x, v_edge[0]->vtx()->y - v_edge[v_edge.size()-1]->vtx()->y);

	//TEST 3: Circle intersects edge
	for(unsigned int i = 0; i < v_edge.size(); ++i)
	{
		static T k;
		k = a_Pc[i].x * a_distCirCenterEdge[i].x + a_Pc[i].y * a_distCirCenterEdge[i].y;

		if (k > Triangulation::EPSILON)
		{
			static T len;
			len = a_distCirCenterEdge[i].x * a_distCirCenterEdge[i].x + a_distCirCenterEdge[i].y * a_distCirCenterEdge[i].y;

			if (k < len)
			{
				if (a_cSqr[i] * len <= k * k)
				{
					s_edges.insert(v_edge[i]);
				}
			}
		}
	}
}
